using System;
using System.Collections;
using System.Collections.Specialized;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Reflection;

namespace Doubler
{

    class VbWriter
    {

        public enum VbMemberType
        {
            Property,
            Sub,
            Function,
            Class
        }
        
#region Fields

        private FileInfo _OutFile;
        private Type _DoubleSubject;
        private Hashtable _Recorders;
        private Hashtable _MemeberNames;

#endregion
        
#region Properties

        public Hashtable MemeberNames
        {
            get
            {
                if (_MemeberNames == null)
                    _MemeberNames = new Hashtable();
                return _MemeberNames;
            }
            set
            {
                _MemeberNames = value;
            }
        }

        public Hashtable Recorders
        {
            get
            {
                if (_Recorders == null)
                    _Recorders = new Hashtable();
                return _Recorders;
            }
            set
            {
                _Recorders = value;
            }
        }

        public FileInfo OutFile
        {
            get
            {
                return _OutFile;
            }
            set
            {
                _OutFile = value;
            }
        }
        public Type DoubleSubject
        {
            get
            {
                return _DoubleSubject;
            }
            set
            {
                _DoubleSubject = value;
            }
        }

#endregion
        
#region Write

        public VbWriter(FileInfo outFile, Type doubleSubject)
        {
            _OutFile = outFile;
            _DoubleSubject = doubleSubject;
        }

        public void WriteDouble()
        {
            using (TextWriter Writer = this.OutFile.CreateText())
            {
                Writer.WriteLine("Option Explicit On");
                Writer.WriteLine("Option Strict On");

                this.WriteImports(Writer);

                Writer.WriteLine(string.Format("Public Class Double{0}", this.DoubleSubject.Name));

                if (this.DoubleSubject.IsInterface)
                {
                    Writer.WriteLine(string.Format("Implements {0}", this.DoubleSubject.FullName));
                }
                else
                {
                    Writer.WriteLine(string.Format("Inherits {0}", this.DoubleSubject.FullName));
                }

                this.BeginRegion("Constructors", Writer);

                this.WriteConstructors(Writer);

                this.EndRegion(Writer);
                this.BeginRegion("Properties", Writer);

                this.WriteProperties(Writer);

                this.EndRegion(Writer);
                this.BeginRegion("Methods", Writer);

                this.WriteMethods(Writer);

                this.EndRegion(Writer);
                this.BeginRegion("Recorders", Writer);

                this.WriteRecorders(Writer);

                this.EndRegion(Writer);

                Writer.WriteLine("End Class");
            }
        }

#endregion

#region Region

        public void BeginRegion(string name, TextWriter writer)
        {
            writer.WriteLine();
            writer.WriteLine(string.Format("#Region \"{0}\"", name));
            writer.WriteLine();
        }

        public void EndRegion(TextWriter writer)
        {
            writer.WriteLine();
            writer.WriteLine("#End Region");
            writer.WriteLine();
        }

#endregion
        
#region Import

        public void WriteImports(TextWriter writer)
        {

        }

#endregion
        
#region Memebers

        public void BeginMember(string visability, VbMemberType memberType, string name, Type returnType, TextWriter writer)
        {
            switch (memberType)
            {
                case VbMemberType.Sub:
                case VbMemberType.Class:
                    writer.WriteLine(String.Format("{0} {1} {2}()", visability, memberType.ToString(), name));
                    break;
                default:
                    writer.WriteLine(String.Format("{0} {1} {2}() As {3}", visability, memberType.ToString(), name, returnType.FullName));
                    break;
            }

        }

        public void BeginMember(string visability, VbMemberType memberType, string name, Type returnType, Type[] parameterTypes, TextWriter writer)
        {
            

            switch (memberType)
            {
                case VbMemberType.Sub:
                case VbMemberType.Class:
                    writer.WriteLine(String.Format("{0} {1} {2}()", visability, memberType.ToString(), name));
                    break;
                default:
                    writer.WriteLine(String.Format("{0} {1} {2}() As {3}", visability, memberType.ToString(), name, returnType.FullName));
                    break;
            }
        }

        public void BeginMember(string visability, string name, VbMemberType memberType, TextWriter writer)
        {
            writer.WriteLine(String.Format("{0} {1} {2}()", visability, memberType.ToString(), name));
        }

        public void EndMember(VbMemberType memberType, TextWriter writer)
        {
            writer.WriteLine(string.Format("End {0}", memberType.ToString()));
            writer.WriteLine();
        }

#endregion

#region Constructor

        private void WriteConstructors(TextWriter writer)
        {
            foreach (ConstructorInfo Constructor in this.DoubleSubject.GetConstructors())
            {
                this.WriteConstructor(Constructor, writer);
            }
            if (this.DoubleSubject.IsInterface)
            {
                this.WriteConstructor(typeof(object).GetConstructors()[0], writer);
            }
        }

        public void WriteConstructor(ConstructorInfo constructor, TextWriter writer)
        {
            StringBuilder ParameterDeclarationBuilder = new StringBuilder();
            StringBuilder ParametersForMyBaseBuilder = new StringBuilder();

            foreach (ParameterInfo Parameter in constructor.GetParameters())
            {
                ParameterDeclarationBuilder.Append(string.Format("{0} As {1}, ", Parameter.Name, Parameter.ParameterType.FullName));
                ParametersForMyBaseBuilder.Append(string.Format("{0}, ", Parameter.Name));
            }

            string ParameterDeclaration = ParameterDeclarationBuilder.ToString().TrimEnd(", ".ToCharArray());
            string ParametersForMyBase = ParametersForMyBaseBuilder.ToString().TrimEnd(", ".ToCharArray());

            //RecorderInfo RecorderInformation = new RecorderInfo("New", ParameterDeclaration, ParametersForMyBase, constructor);
            //this.AddToRecorders("New", RecorderInformation);

            string ConstrautorDeclaration = string.Format("Public Sub New({0})", ParameterDeclaration);
            writer.WriteLine(ConstrautorDeclaration);

            string CallMyBase = string.Format("MyBase.New({0})", ParametersForMyBase);
            writer.WriteLine(CallMyBase);

            writer.WriteLine("End Sub");
            writer.WriteLine();
        }

#endregion
        
#region Property

        private void WriteProperties(TextWriter writer)
        {
            bool Abstract;
            string FieldName;
            bool Read;
            bool Write;
            MethodAttributes Visability;
            foreach (PropertyInfo Property in this.DoubleSubject.GetProperties())
            {
                Read = false;
                Write = false;
                FieldName = string.Empty;
                Visability = MethodAttributes.Public;

                if (Property.GetSetMethod() != null)
                {
                    Abstract = false;
                    Abstract = Property.GetSetMethod().IsAbstract;
                    if (Abstract)
                    {
                        Write = true;
                        Visability = (Property.GetSetMethod().Attributes & MethodAttributes.Public) | (Property.GetSetMethod().Attributes & MethodAttributes.Private) | (Property.GetSetMethod().Attributes & MethodAttributes.Family) | (Property.GetSetMethod().Attributes & MethodAttributes.Assembly);
                    }
                }

                if (Property.GetGetMethod() != null)
                {
                    Abstract = false;
                    Abstract = Property.GetGetMethod().IsAbstract;
                    if (Abstract)
                    {
                        Read = true;
                        Visability = (Property.GetGetMethod().Attributes & MethodAttributes.Public) | (Property.GetGetMethod().Attributes & MethodAttributes.Private) | (Property.GetGetMethod().Attributes & MethodAttributes.Family) | (Property.GetGetMethod().Attributes & MethodAttributes.Assembly);
                    }
                }

                if (Read || Write)
                {
                    FieldName = string.Format("_{0}", Property.Name);
                    this.WriteField(FieldName, Property.PropertyType, writer);

                    this.BeginMember(Visable.Map(Visability), VbMemberType.Property, Property.Name, Property.PropertyType, writer);
                    
                    if (Write)
                        this.WritePropertySetter(Property, FieldName, writer);
                    if (Read)
                        this.WritePropertyGetter(Property, FieldName, writer);

                    this.EndMember(VbMemberType.Property, writer);
                }
                
            }
        }

        

        public void WritePropertySetter(PropertyInfo property, string fieldName, TextWriter writer)
        {
            //Set(ByVal Value As Subscriber)
            //  _testSubscriber = Value
            //End Set
            writer.WriteLine(string.Format("Set(ByVal Value As {0})", property.PropertyType.FullName));
            writer.WriteLine(string.Format("{0} = Value", fieldName));
            writer.WriteLine("End Set");
        }

        public void WritePropertyGetter(PropertyInfo property, string fieldName, TextWriter writer)
        {
            writer.WriteLine("Get");
            writer.WriteLine(string.Format("Return {0}", fieldName));
            writer.WriteLine("End Get");
        }

#endregion
        
#region Field

        public void WriteField(string name, Type type, TextWriter writer)
        {
            writer.WriteLine(string.Format("Private {0} As {1}", name, type.FullName));
        }

#endregion
        
#region Method

        private void WriteMethods(TextWriter writer)
        {
            bool Abstract = false;
            foreach (MethodInfo Method in this.DoubleSubject.GetMethods())
            {
                Abstract = (Method.Attributes & MethodAttributes.Abstract) == MethodAttributes.Abstract;
                if (Abstract)
                {
                    this.WriteMethod(Method, writer);
                }
            }
        }

        public void WriteMethod(MethodInfo method, TextWriter writer)
        {

        }

#endregion
        
#region Recorders

        public void AddToRecorders(string name, RecorderInfo recorderInformation)
        {
            if (this.MemeberNames.Contains(name))
            {
                int Num = (int)this.MemeberNames[name];
                this.MemeberNames[name] = Num + 1;
                name = string.Format("{0}{1}", name, Num);
                recorderInformation.Name = name;
            }
            else
            {
                this.MemeberNames.Add(name, 1);
            }
            this.Recorders.Add(name, recorderInformation);
        }

        public void WriteRecorders(TextWriter writer)
        {

        }

#endregion
    
        private class Visable
        {
            private static Hashtable _Table = new Hashtable();

            public static string Map(MethodAttributes visability)
            {
                return (String)_Table[visability];
            }

            static Visable()
            {
                _Table.Add(MethodAttributes.Assembly, "Friend");
                _Table.Add(MethodAttributes.Family, "Protected");
                _Table.Add(MethodAttributes.Private, "Private");
                _Table.Add(MethodAttributes.Public, "Public");
            }



        }
    }
}
